// Scene Manager
// (c) jimon game studio

#include "CSceneManager.h"
#include "jeSceneNodeCreators.h"
#include "jeAnimatorCreators.h"
#include "jeCompositorCreators.h"

#include "IVariables.h"
#include "ILogManager.h"
#include "IProfiler.h"

namespace je
{
	namespace scene
	{
		//! Constructor
		CSceneManager::CSceneManager(video::IRender * Render,BaseSceneManagerCompositors CompositorType)
			:CurrentRender(Render),PreviousCompositor(NULL),CurrentCamera(NULL),CurrentLayerControl(false)
		{
			CurrentRender->Grab();

			JENEW(NodeFactory,NodeFactoryType)
			JENEW(AnimatorFactory,AnimatorFactoryType)
			JENEW(CompositorFactory,CompositorFactoryType)

			for(u32 i=0;i<RPG_MAX;i++)
				PassGroups[i] = NULL;

			JENEW(LightList,LightListType)
	
			RegisterNodeTypes(NodeFactory,CurrentRender,this);
			RegisterAnimTypes(AnimatorFactory,this);
			RegisterCompositorTypes(CompositorFactory,this);

			Root = NodeFactory->Create("RootSceneNode");

			JENEW(CompositorList,CompositorListType)

			ICompositor * DefaultCompositor = NULL;
			switch(CompositorType)
			{
			case BSMC_DEFAULT:
				JENEW(DefaultCompositor,CDefaultCompositor(this))
				break;
			case BSMC_FORWARD:
				JENEW(DefaultCompositor,CForwardCompositor(this))
				break;
			case BSMC_DEFERRED:
				JENEW(DefaultCompositor,CDeferredShadingCompositor(this))
				break;
			default:
				JELOG_E("Unknown base compositor type")
				break;
			}
			CompositorList->push_back(DefaultCompositor);
		}

		//! Destructor
		CSceneManager::~CSceneManager()
		{
			SetCurrentCamera(NULL);

			for(CompositorListType::Iterator Iter = CompositorList->begin();Iter!=CompositorList->end();Iter++)
				(*Iter)->Drop();
			CompositorList->clear();
			JEDELETE(CompositorList)

			Root->Drop();

			JEDELETE(LightList)

			for(u32 i=0;i<RPG_MAX;i++)
				if(PassGroups[i])
					JEDELETE(PassGroups[i])

			JEDELETE(CompositorFactory)
			JEDELETE(AnimatorFactory)
			JEDELETE(NodeFactory)

			CurrentRender->Drop();
		}

		//! Create Camera Scene Node
		ICameraSceneNode * CSceneManager::CreateCamera(ISceneNode * Parent)
		{
			ICameraSceneNode * Node = NULL;
			JENEW(Node,CCameraSceneNode(CurrentRender))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Create Sprite Scene Node
		ISpriteSceneNode * CSceneManager::CreateSprite(ISceneNode * Parent)
		{
			ISpriteSceneNode * Node = NULL;
			JENEW(Node,CSpriteSceneNode(CurrentRender,this))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Create Pivot Scene Node
		ISceneNode * CSceneManager::CreatePivot(ISceneNode * Parent)
		{
			ISceneNode * Node = NULL;
			JENEW(Node,CPivotSceneNode(CurrentRender))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Create Debug Scene Node
		IDebugSceneNode * CSceneManager::CreateDebug(ISceneNode * Parent)
		{
			IDebugSceneNode * Node = NULL;
			JENEW(Node,CDebugSceneNode(CurrentRender,this))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Create Text Scene Node
		ITextSceneNode * CSceneManager::CreateText(const jeStringd & Text,ISceneNode * Parent)
		{
			ITextSceneNode * Node = NULL;
			JENEW(Node,CTextSceneNode(CurrentRender,this))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->SetText(Text);

			Node->Drop();
			return Node;
		}

		//! Create Mesh Scene Node
		IMeshSceneNode * CSceneManager::CreateMeshNode(ISceneNode * Parent)
		{
			IMeshSceneNode * Node = NULL;
			JENEW(Node,CMeshSceneNode(CurrentRender,this))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Create Light Scene Node
		ILightSceneNode * CSceneManager::CreateLight(ISceneNode * Parent)
		{
			ILightSceneNode * Node = NULL;
			JENEW(Node,CLightSceneNode(CurrentRender,this))

			if(Parent == NULL)
				Root->AddChildren(Node);
			else
				Parent->AddChildren(Node);

			Node->Drop();
			return Node;
		}

		//! Set Current Camera
		void CSceneManager::SetCurrentCamera(ICameraSceneNode * Node)
		{
			if(CurrentCamera)
				CurrentCamera->Drop();
			CurrentCamera = Node;
			if(CurrentCamera)
				CurrentCamera->Grab();
		}

		//! Get Current Camera
		ICameraSceneNode * CSceneManager::GetCurrentCamera()
		{
			return CurrentCamera;
		}

		//! Set Layer Control
		void CSceneManager::SetLayerControl(u1 Enable)
		{
			CurrentLayerControl = Enable;
		}

		//! Get Layer Control
		u1 CSceneManager::GetLayerControl()
		{
			return CurrentLayerControl;
		}

		//! Swap Layers
		void CSceneManager::SwapLayers(s32 Layer1,s32 Layer2)
		{
			SwapLayers(Root,Layer1,Layer2);
		}

		//! Return Scene Manager Render
		video::IRender * CSceneManager::GetRender()
		{
			return CurrentRender;
		}

		//! Return Root
		ISceneNode * CSceneManager::GetRoot()
		{
			return Root;
		}

		//! Get Node By Name
		ISceneNode * CSceneManager::GetNodeByName(const jeStringd & NodeName)
		{
			return Root->GetNodeByName(NodeName);
		}

		//! On Update
		void CSceneManager::OnUpdate()
		{
			JEP_BEG("SceneManager Update")

			JEP_BEG("SceneManager Update - Update Root")
			Root->OnUpdate();
			JEP_END("SceneManager Update - Update Root")

			JEP_BEG("SceneManager Update - Build Matrixes")
			Root->BuildAbsoluteModelMatrix(jeMatrix4f(),jeVector3df(1.0f),false);
			JEP_END("SceneManager Update - Build Matrixes")

			JEP_BEG("SceneManager Update - Visibility Culling")
			Root->OnVisibilityCulling();
			JEP_END("SceneManager Update - Visibility Culling")
			
			JEP_BEG("SceneManager Update - Sort Compositors")
			// sort compositors by priority
			// this quite stupid, but i dont know how to better
			// because for example layer scene node has layer manager, and layer manager sort list by priority
			// but icompositor cant use scene manager for this purposes
			u1 RunSort = true;
			while(RunSort)
			{
				RunSort = false;
				for(CompositorListType::Iterator Iter = CompositorList->begin()+1;Iter!=CompositorList->end();Iter++)
				{
					CompositorListType::Iterator IterBack = Iter-1;
					if((*IterBack)->GetPriority() > (*Iter)->GetPriority())
					{
						CompositorList->insert_after(Iter,*IterBack);
						CompositorList->erase(IterBack);
						Iter++;
						RunSort = true;
					}
				}
			}
			JEP_END("SceneManager Update - Sort Compositors")

			JEP_BEG("SceneManager Update - Sort Pass Groups")
			for(u32 i=0;i<RPG_MAX;i++)
				if(PassGroups[i])
					PassGroups[i]->Sort(CurrentCamera,CurrentLayerControl);
			JEP_END("SceneManager Update - Sort Pass Groups")


			JEP_END("SceneManager Update")
		}

		//! On Render
		void CSceneManager::OnRender()
		{
			JEP_BEG("SceneManager Render")

			JEP_BEG("SceneManager Render - Render Compositors")

			PreviousCompositor = NULL;

			for(CompositorListType::Iterator Iter = CompositorList->begin();Iter!=CompositorList->end();Iter++)
			{
				(*Iter)->OnRender();
				PreviousCompositor = (*Iter);
			}

			JEP_END("SceneManager Render - Render Compositors")

			JEP_END("SceneManager Render")
		}
		
		//! Register Node In Rendering Queue
		void CSceneManager::RegisterNodeInRenderingQueue(ISceneNode * Node,RenderPassGroup Group,RenderPassGroupRights Rights)
		{
			if(PassGroups[Group])
				PassGroups[Group]->AddNode(Node);
			else
			{
				jeRenderPassGroupQueue * NewGroup = NULL;
				JENEW(NewGroup,jeRenderPassGroupQueue(Group,Rights))
				NewGroup->AddNode(Node);
				PassGroups[Group] = NewGroup;
			}
		}

		//! UnRegister Node From Rendering Queue
		void CSceneManager::UnRegisterNodeFromRenderingQueue(ISceneNode * Node,RenderPassGroup Group)
		{
			if(PassGroups[Group] && PassGroups[Group]->RemoveNode(Node))
				if(!PassGroups[Group]->GetNodeCount())
				{
					JEDELETE(PassGroups[Group])
					PassGroups[Group] = NULL;
				}
		}

		//! Render Queue Groups
		void CSceneManager::RenderQueueGroups(u1 RenderAllGroups,RenderPassGroup Group)
		{
			if(RenderAllGroups)
			{
				for(u32 i=0;i<RPG_MAX;i++)
					if(PassGroups[i])
						PassGroups[i]->OnRender();
			}
			else
				if(PassGroups[Group])
					PassGroups[Group]->OnRender();
		}

		//! Render Queue Object
		void CSceneManager::RenderQueueObject(u32 ObjectQueueIndex,RenderPassGroup Group)
		{
			if(PassGroups[Group])
				PassGroups[Group]->OnRender(ObjectQueueIndex);
		}

		//! Get Count Of Objects In Queue Group
		u32 CSceneManager::GetCountOfObjectsInQueueGroup(RenderPassGroup Group)
		{
			if(PassGroups[Group])
				return PassGroups[Group]->GetNodeCount();
			else
				return 0;
		}

		//! Set Render Pass Group Rights
		void CSceneManager::SetRenderPassGroupRights(RenderPassGroup Group,RenderPassGroupRights Rights)
		{
			if(PassGroups[Group])
				PassGroups[Group]->SetRights(Rights);
		}

		//! Add Compositor
		void CSceneManager::AddCompositor(ICompositor * Compositor)
		{
			CompositorList->push_back(Compositor);
			Compositor->Grab();
		}

		//! Remove Compositor
		void CSceneManager::RemoveCompositor(ICompositor * Compositor)
		{
			for(CompositorListType::Iterator Iter = CompositorList->begin();Iter!=CompositorList->end();Iter++)
				if((*Iter) == Compositor)
				{
					Compositor->Drop();
					CompositorList->erase(Iter);
					return;
				}
		}

		//! Get Compositors List
		ISceneManager::CompositorListType * CSceneManager::GetCompositorList()
		{
			return CompositorList;
		}

		//! Get Previous Compositor
		ICompositor * CSceneManager::GetPreviousCompositor()
		{
			return PreviousCompositor;
		}

		//! Return Factory
		ISceneManager::NodeFactoryType * CSceneManager::GetNodeFactory()
		{
			return NodeFactory;
		}

		//! Return Animator Factory
		ISceneManager::AnimatorFactoryType * CSceneManager::GetAnimatorFactory()
		{
			return AnimatorFactory;
		}

		//! Return Compositor Factory
		ISceneManager::CompositorFactoryType * CSceneManager::GetCompositorFactory()
		{
			return CompositorFactory;
		}

		//! Register Light
		void CSceneManager::RegisterLight(ILightSceneNode * Light)
		{
			LightList->push_back(Light);
		}

		//! UnRegister Light
		void CSceneManager::UnRegisterLight(ILightSceneNode * Light)
		{
			for(LightListType::Iterator Iter = LightList->begin();Iter!=LightList->end();Iter++)
				if((*Iter) == Light)
				{
					LightList->erase(Iter);
					return;
				}
		}

		//! Get Light List
		CSceneManager::LightListType * CSceneManager::GetLightList()
		{
			return LightList;
		}

		//! Serialize
		void CSceneManager::Serialize(core::ISerializeStream * SerializeStream)
		{
			if(JE_SS_LOAD)
			{
				for(CompositorListType::Iterator Iter = CompositorList->begin();Iter!=CompositorList->end();Iter++)
					(*Iter)->Drop();
				CompositorList->clear();

				SetCurrentCamera(NULL);
				Root->Drop();
				Root = NULL;
			}

			JE_SS_BEGIN(Nodes)
			Serialize(Root,SerializeStream);
			JE_SS_END(Nodes)

			JE_SS_BEGIN(Compositors)

			if(JE_SS_SAVE)
			{
				for(CompositorListType::Iterator Iter = CompositorList->begin();Iter!=CompositorList->end();Iter++)
				{
					JE_SS_NEW(Compositor)

					jeStringc CompositorType = (*Iter)->GetCompositorTypeName();
					JE_SS(CompositorType)

					JE_SS_BEGIN(CompositorData)
					(*Iter)->Serialize(SerializeStream);
					JE_SS_END(CompositorData)

					JE_SS_END(Compositor)
				}
			}
			else
			{
				u1 Run = true;
				JE_SS_BEGIN(Compositor)
				while(Run)
				{
					jeStringc CompositorType;
					JE_SS(CompositorType)

					ICompositor * Compositor = CompositorFactory->Create(CompositorType);

					JE_SS_BEGIN(CompositorData)
					Compositor->Serialize(SerializeStream);
					JE_SS_END(CompositorData)

					CompositorList->push_back(Compositor);

					Run = JE_SS_NEXT;
				}
				JE_SS_END(Compositor)
			}
			
			JE_SS_END(Compositors)

			JE_SS_BEGIN(Options)
			JE_SS(CurrentLayerControl)
			JE_SS_END(Options)
		}

		//! Serialize
		void CSceneManager::Serialize(ISceneNode * Node,core::ISerializeStream * SerializeStream)
		{
			if(JE_SS_SAVE)
			{
				JE_SS_NEW(SceneNodeBase)

				jeStringc NodeType = Node->GetNodeTypeName();
				JE_SS(NodeType)

				u32 ChildrensCount = Node->GetChildrensList().getSize();
				JE_SS(ChildrensCount)

				u32 AnimatorsCount = Node->GetAnimatorsList().getSize();
				JE_SS(AnimatorsCount)

				u1 ThisIsCurrentCamera = Node == CurrentCamera;
				JE_SS(ThisIsCurrentCamera)

				JE_SS_BEGIN(SceneNodeData)
				Node->Serialize(SerializeStream);
				JE_SS_END(SceneNodeData)

				if(AnimatorsCount)
				{
					JE_SS_BEGIN(SceneNodeAnimators)
					for(ISceneNode::AnimatorsListType::Iterator Iter = Node->GetAnimatorsList().begin();Iter != Node->GetAnimatorsList().end();Iter++)
					{
						JE_SS_NEW(Animator)
						jeStringc AnimatorType = (*Iter)->GetAnimatorTypeName();
						JE_SS(AnimatorType)
						JE_SS_BEGIN(AnimatorsData)
						(*Iter)->Serialize(SerializeStream);
						JE_SS_END(AnimatorsData)
						JE_SS_END(Animator)
					}
					JE_SS_END(SceneNodeAnimators)
				}

				for(ISceneNode::ChildrensListType::Iterator Iter = Node->GetChildrensList().begin();Iter != Node->GetChildrensList().end();Iter++)
					Serialize(*Iter,SerializeStream);

				JE_SS_END(SceneNodeBase)
			}
			else
			{
				u1 Run = true;
				JE_SS_BEGIN(SceneNodeBase)
				while (Run)
				{
					jeStringc NodeType;
					JE_SS(NodeType)

					ISceneNode * CurrentNode = NodeFactory->Create(NodeType);

					if(Node == NULL)
						Root = CurrentNode;
					else
					{
						Node->AddChildren(CurrentNode);
						CurrentNode->Drop();
					}

					u32 ChildrensCount;
					JE_SS(ChildrensCount)

					u32 AnimatorsCount;
					JE_SS(AnimatorsCount)

					u1 ThisIsCurrentCamera;
					JE_SS(ThisIsCurrentCamera)

					if(ThisIsCurrentCamera)
					{
						CurrentCamera = static_cast<ICameraSceneNode*>(CurrentNode);
						CurrentCamera->Grab();
					}

					JE_SS_BEGIN(SceneNodeData)
					CurrentNode->Serialize(SerializeStream);
					JE_SS_END(SceneNodeData)

					if(AnimatorsCount)
					{
						JE_SS_BEGIN(SceneNodeAnimators)

						u1 RunAnimator = true;
						JE_SS_BEGIN(Animator)
						while(RunAnimator)
						{
							jeStringc AnimatorType;
							JE_SS(AnimatorType)
							IAnimator * CurrentAnimator = AnimatorFactory->Create(AnimatorType);

							JE_SS_BEGIN(AnimatorsData)
							CurrentAnimator->Serialize(SerializeStream);
							JE_SS_END(AnimatorsData)

							CurrentNode->AddAnimator(CurrentAnimator);
							CurrentAnimator->Drop();

							RunAnimator = JE_SS_NEXT;
						}
						JE_SS_END(Animator)

						JE_SS_END(SceneNodeAnimators)
					}

					if(ChildrensCount)
						Serialize(CurrentNode,SerializeStream);

					Run = JE_SS_NEXT;
				}
				JE_SS_END(SceneNodeBase)
			}
		}

		//! Swap Layers
		void CSceneManager::SwapLayers(ISceneNode * Node,s32 Layer1,s32 Layer2)
		{
			if(Node->GetLayer() == Layer1)
				Node->SetLayer(Layer2);
			else
				if(Node->GetLayer() == Layer2)
					Node->SetLayer(Layer1);

			for(ISceneNode::ChildrensListType::Iterator Iter = Node->GetChildrensList().begin();Iter != Node->GetChildrensList().end();Iter++)
				SwapLayers(*Iter,Layer1,Layer2);
		}

		//! Create SceneManager
		ISceneManager * JE_ENGINE_FUNCTION_CALL CreateSceneManager(video::IRender * Render,BaseSceneManagerCompositors Type)
		{
			ISceneManager * SceneManager = NULL;
			JENEW(SceneManager,CSceneManager(Render,Type))
			return SceneManager;
		}
	}
}
